package com.spring.demo5.factory.config;

import com.spring.demo4.factory.advice.MyAdvice;
import com.spring.demo4.factory.advisor.MyAdvisor;
import org.aopalliance.aop.Advice;
import org.springframework.aop.*;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AdvisorChainFactory;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.aop.framework.DefaultAdvisorChainFactory;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.target.EmptyTargetSource;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * AOP代理配置管理器的基类，继承了 ProxyConfig 并且实现了 Advised 接口，创建aop代理之前，所有需要配置的信息都是通过这个类来操作的
 *
 * @author vhans
 */
public class MyAdvisedSupport extends MyProxyConfig implements MyAdvised {
    private static final long serialVersionUID = 2651364800145442165L;

    public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;

    TargetSource targetSource = EMPTY_TARGET_SOURCE;

    /**
     * 建议器是否已经针对特定的目标类进行筛选
     */
    private boolean preFiltered = false;

    /**
     * 调用链工厂，用来获取目标方法的调用链
     */
    AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();

    /**
     * 方法调用链缓存：以方法为键，以顾问链表为值的缓存
     * <br>
     * 目标方法和其关联的方法拦截器列表会被缓存在 methodCache 中，当顾问列表有变化的时候，methodCache 缓存会被清除
     */
    private transient Map<MyAdvisedSupport.MethodCacheKey, List<Object>> methodCache;

    /**
     * 代理对象需要实现的接口列表。保存在列表中以保持注册的顺序，以创建具有指定接口顺序的JDK代理
     */
    private List<Class<?>> interfaces = new ArrayList<>();

    /**
     * 配置的顾问列表。所有添加的Advise对象都会被包装为Advisor对象
     */
    private List<MyAdvisor> advisors = new ArrayList<>();

    /**
     * 数组更新了对advisor列表的更改，这更容易在内部操作
     */
    private MyAdvisor[] advisorArray = new MyAdvisor[0];


    /**
     * 无参构造方法
     */
    public MyAdvisedSupport() {
        this.methodCache = new ConcurrentHashMap<>(32);
    }

    /**
     * 有参构造方法，参数为：代理需要实现的接口列表
     */
    public MyAdvisedSupport(Class<?>... interfaces) {
        this();
        setInterfaces(interfaces);
    }


    /**
     * 设置需要被代理的目标对象，目标对象会被包装为TargetSource格式的对象
     */
    public void setTarget(Object target) {
        setTargetSource(new SingletonTargetSource(target));
    }

    /**
     * 设置被代理的目标源
     */
    @Override
    public void setTargetSource(@Nullable TargetSource targetSource) {
        this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
    }

    /**
     * 获取被代理的目标源
     */
    @Override
    public TargetSource getTargetSource() {
        return this.targetSource;
    }

    /**
     * 设置被代理的目标类
     */
    public void setTargetClass(@Nullable Class<?> targetClass) {
        this.targetSource = EmptyTargetSource.forClass(targetClass);
    }

    /**
     * 获取被代理的目标类型
     */
    @Override
    @Nullable
    public Class<?> getTargetClass() {
        return this.targetSource.getTargetClass();
    }

    /**
     * 设置此代理配置是否经过预筛选，就是通过目标方法调用代理的时候，需要通过匹配的方式获取这个方法上的调用链列表，查找过程需要2个步骤：
     * 第一步：类是否匹配
     * 第二步：方法是否匹配，当这个属性为true的时候，会直接跳过第一步
     */
    @Override
    public void setPreFiltered(boolean preFiltered) {
        this.preFiltered = preFiltered;
    }

    /**
     * 返回PreFiltered
     */
    @Override
    public boolean isPreFiltered() {
        return this.preFiltered;
    }

    /**
     * 设置顾问链工厂，当调用目标方法的时候，需要获取这个方法上匹配的Advisor列表，
     * 获取目标方法上匹配的Advisor列表的功能就是AdvisorChainFactory来负责的
     */
    public void setAdvisorChainFactory(AdvisorChainFactory advisorChainFactory) {
        Assert.notNull(advisorChainFactory, "AdvisorChainFactory must not be null");
        this.advisorChainFactory = advisorChainFactory;
    }

    /**
     * 返回顾问链工厂对象
     */
    public AdvisorChainFactory getAdvisorChainFactory() {
        return this.advisorChainFactory;
    }


    /**
     * 设置代理对象需要实现的接口
     */
    public void setInterfaces(Class<?>... interfaces) {
        Assert.notNull(interfaces, "Interfaces must not be null");
        this.interfaces.clear();
        for (Class<?> ifc : interfaces) {
            addInterface(ifc);
        }
    }

    /**
     * 为代理对象添加需要实现的接口
     */
    public void addInterface(Class<?> intf) {
        Assert.notNull(intf, "Interface must not be null");
        if (!intf.isInterface()) {
            throw new IllegalArgumentException("[" + intf.getName() + "] is not an interface");
        }
        if (!this.interfaces.contains(intf)) {
            this.interfaces.add(intf);
            adviceChanged();
        }
    }

    /**
     * 移除代理对象需要实现的接口
     */
    public boolean removeInterface(Class<?> intf) {
        return this.interfaces.remove(intf);
    }

    /**
     * 获取代理对象需要实现的接口列表
     */
    @Override
    public Class<?>[] getProxiedInterfaces() {
        return ClassUtils.toClassArray(this.interfaces);
    }

    /**
     * 判断代理对象是否需要实现某个接口
     */
    @Override
    public boolean isInterfaceProxied(Class<?> intf) {
        for (Class<?> proxyIntf : this.interfaces) {
            if (intf.isAssignableFrom(proxyIntf)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取配置的所有顾问列表
     */
    @Override
    public final MyAdvisor[] getAdvisors() {
        return this.advisorArray;
    }

    /**
     * 添加顾问
     */
    @Override
    public void addAdvisor(MyAdvisor advisor) {
        int pos = this.advisors.size();
        addAdvisor(pos, advisor);
    }

    /**
     * 指定位置添加顾问
     */
    @Override
    public void addAdvisor(int pos, MyAdvisor advisor) throws AopConfigException {
        if (advisor instanceof IntroductionAdvisor) {
            validateIntroductionAdvisor((IntroductionAdvisor) advisor);
        }
        addAdvisorInternal(pos, advisor);
    }

    /**
     * 移除顾问
     */
    @Override
    public boolean removeAdvisor(MyAdvisor advisor) {
        int index = indexOf(advisor);
        if (index == -1) {
            return false;
        } else {
            removeAdvisor(index);
            return true;
        }
    }

    /**
     * 移除指定位置的顾问
     */
    @Override
    public void removeAdvisor(int index) throws AopConfigException {
        // 当配置如果是冻结状态，是不允许对顾问进行修改的，否则会抛出异常
        if (isFrozen()) {
            throw new AopConfigException("Cannot remove Advisor: Configuration is frozen.");
        }
        if (index < 0 || index > this.advisors.size() - 1) {
            throw new AopConfigException("Advisor index " + index + " is out of bounds: " +
                    "This configuration only has " + this.advisors.size() + " advisors.");
        }
        // 移除advisors中的顾问
        MyAdvisor advisor = this.advisors.remove(index);
        if (advisor instanceof IntroductionAdvisor) {
            IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
            // We need to remove introduction interfaces.
            for (Class<?> ifc : ia.getInterfaces()) {
                removeInterface(ifc);
            }
        }
        // 更新advisorArray
        updateAdvisorArray();
        // 通知已改变，内部会清除方法调用链缓存信息
        adviceChanged();
    }

    /**
     * 获取指定顾问的索引
     */
    @Override
    public int indexOf(MyAdvisor advisor) {
        Assert.notNull(advisor, "Advisor must not be null");
        return this.advisors.indexOf(advisor);
    }

    /**
     * 更换顾问
     */
    @Override
    public boolean replaceAdvisor(MyAdvisor a, MyAdvisor b) throws AopConfigException {
        Assert.notNull(a, "Advisor a must not be null");
        Assert.notNull(b, "Advisor b must not be null");
        int index = indexOf(a);
        if (index == -1) {
            return false;
        }
        removeAdvisor(index);
        addAdvisor(index, b);
        return true;
    }

    /**
     * 将所有给定的顾问添加到此代理配置中
     */
    public void addAdvisors(MyAdvisor... advisors) {
        addAdvisors(Arrays.asList(advisors));
    }

    /**
     * 将所有给定的顾问添加到此代理配置中
     */
    public void addAdvisors(Collection<MyAdvisor> advisors) {
        // 配置如果是冻结状态，会抛出异常
        if (isFrozen()) {
            throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
        }
        if (!CollectionUtils.isEmpty(advisors)) {
            for (MyAdvisor advisor : advisors) {
                if (advisor instanceof IntroductionAdvisor) {
                    validateIntroductionAdvisor((IntroductionAdvisor) advisor);
                }
                Assert.notNull(advisor, "Advisor must not be null");
                this.advisors.add(advisor);
            }
            updateAdvisorArray();
            adviceChanged();
        }
    }

    /**
     * 用来为目标类引入接口的
     */
    private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
        advisor.validateInterfaces();
        // 如果advisor通过验证，我们就可以进行更改
        Class<?>[] ifcs = advisor.getInterfaces();
        for (Class<?> ifc : ifcs) {
            addInterface(ifc);
        }
    }

    /**
     * 指定的位置添加顾问
     */
    private void addAdvisorInternal(int pos, MyAdvisor advisor) throws AopConfigException {
        Assert.notNull(advisor, "Advisor must not be null");
        if (isFrozen()) {
            throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
        }
        if (pos > this.advisors.size()) {
            throw new IllegalArgumentException(
                    "Illegal position " + pos + " in advisor list with size " + this.advisors.size());
        }
        this.advisors.add(pos, advisor);
        updateAdvisorArray();
        adviceChanged();
    }

    /**
     * 将advisorArray和advisors保持一致
     */
    protected final void updateAdvisorArray() {
        this.advisorArray = this.advisors.toArray(new MyAdvisor[0]);
    }

    /**
     * 获取顾问列表
     */
    protected final List<MyAdvisor> getAdvisorsInternal() {
        return this.advisors;
    }


    /**
     * 添加通知
     */
    @Override
    public void addAdvice(MyAdvice advice) throws AopConfigException {
        int pos = this.advisors.size();
        addAdvice(pos, advice);
    }

    /**
     * 指定位置添加通知
     */
    @Override
    public void addAdvice(int pos, MyAdvice advice) throws AopConfigException {
        Assert.notNull(advice, "Advice must not be null");
        if (advice instanceof IntroductionInfo) {
            // 此处会将advice通知包装为DefaultIntroductionAdvisor类型的Advisor
            addAdvisor(pos, (MyAdvisor) new DefaultIntroductionAdvisor((Advice) advice, (IntroductionInfo) advice));
        } else if (advice instanceof DynamicIntroductionAdvice) {
            throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
        } else {
            // 此处会将advice通知包装为DefaultPointcutAdvisor类型的Advisor
            // DefaultPointcutAdvisor未指定pointcut，pointcut采用默认值，默认会匹配任意类的任意方法
            addAdvisor(pos, (MyAdvisor) new DefaultPointcutAdvisor((Advice) advice));
        }
    }

    /**
     * 移除通知
     */
    @Override
    public boolean removeAdvice(MyAdvice advice) throws AopConfigException {
        int index = indexOf(advice);
        if (index == -1) {
            return false;
        } else {
            removeAdvisor(index);
            return true;
        }
    }

    /**
     * 返回通知的索引
     */
    @Override
    public int indexOf(MyAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        for (int i = 0; i < this.advisors.size(); i++) {
            MyAdvisor advisor = this.advisors.get(i);
            if (advisor.getAdvice() == advice) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 给定的通知是否包含在此代理配置中的任何顾问中
     */
    public boolean adviceIncluded(@Nullable MyAdvice advice) {
        if (advice != null) {
            for (MyAdvisor advisor : this.advisors) {
                if (advisor.getAdvice() == advice) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获取当前配置中某种类型通知的数量
     */
    public int countAdvicesOfType(@Nullable Class<?> adviceClass) {
        int count = 0;
        if (adviceClass != null) {
            for (MyAdvisor advisor : this.advisors) {
                if (adviceClass.isInstance(advisor.getAdvice())) {
                    count++;
                }
            }
        }
        return count;
    }


    /**
     * 基于当前配置，获取给定方法的方法调用链列表（即org.aopalliance.intercept.MethodInterceptor对象列表）
     * <br>
     * 通过代理调用目标方法的时候，最后需要通过方法和目标类的类型，从当前配置中会获取匹配的方法拦截器列表，
     * 获取方法拦截器列表是由 AdvisorChainFactory 负责的
     */
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
        MyAdvisedSupport.MethodCacheKey cacheKey = new MyAdvisedSupport.MethodCacheKey(method);
        // 先从缓存中获取
        List<Object> cached = this.methodCache.get(cacheKey);
        // 缓存中没有时，从advisorChainFactory中获取
        if (cached == null) {
            // getInterceptorsAndDynamicInterceptionAdvice方法在调用代理的方法时会执行
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                    (Advised) this, method, targetClass);
            this.methodCache.put(cacheKey, cached);
        }
        return cached;
    }

    /**
     * 通知更改时调用，会清空当前方法调用链缓存
     */
    protected void adviceChanged() {
        this.methodCache.clear();
    }

    /**
     * 将other中的配置信息复制到当前对象中
     */
    protected void copyConfigurationFrom(MyAdvisedSupport other) {
        copyConfigurationFrom(other, other.targetSource, new ArrayList<>(other.advisors));
    }

    /**
     * 将other中的配置信息复制到当前对象中
     */
    protected void copyConfigurationFrom(MyAdvisedSupport other, TargetSource targetSource, List<MyAdvisor> advisors) {
        copyFrom(other);
        this.targetSource = targetSource;
        this.advisorChainFactory = other.advisorChainFactory;
        this.interfaces = new ArrayList<>(other.interfaces);
        for (MyAdvisor advisor : advisors) {
            if (advisor instanceof IntroductionAdvisor) {
                validateIntroductionAdvisor((IntroductionAdvisor) advisor);
            }
            Assert.notNull(advisor, "Advisor must not be null");
            this.advisors.add(advisor);
        }
        updateAdvisorArray();
        adviceChanged();
    }

    /**
     * 构建此AdvisedSupport的仅配置副本，替换TargetSource
     */
    MyAdvisedSupport getConfigurationOnlyCopy() {
        MyAdvisedSupport copy = new MyAdvisedSupport();
        copy.copyFrom(this);
        copy.targetSource = EmptyTargetSource.forClass(getTargetClass(), getTargetSource().isStatic());
        copy.advisorChainFactory = this.advisorChainFactory;
        copy.interfaces = this.interfaces;
        copy.advisors = this.advisors;
        copy.updateAdvisorArray();
        return copy;
    }


    //---------------------------------------------------------------------
    // 序列化支持
    //---------------------------------------------------------------------

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        // Rely on default serialization; just initialize state after deserialization.
        ois.defaultReadObject();

        // Initialize transient fields.
        this.methodCache = new ConcurrentHashMap<>(32);
    }


    @Override
    public String toProxyConfigString() {
        return toString();
    }

    /**
     * 用于调试/诊断
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(getClass().getName());
        sb.append(": ").append(this.interfaces.size()).append(" interfaces ");
        sb.append(ClassUtils.classNamesToString(this.interfaces)).append("; ");
        sb.append(this.advisors.size()).append(" advisors ");
        sb.append(this.advisors).append("; ");
        sb.append("targetSource [").append(this.targetSource).append("]; ");
        sb.append(super.toString());
        return sb.toString();
    }


    /**
     * 方法周围的简单包装器类。用作缓存方法时的键，用于高效的equals和hashCode比较
     */
    private static final class MethodCacheKey implements Comparable<MyAdvisedSupport.MethodCacheKey> {

        private final Method method;

        private final int hashCode;

        public MethodCacheKey(Method method) {
            this.method = method;
            this.hashCode = method.hashCode();
        }

        @Override
        public boolean equals(@Nullable Object other) {
            return (this == other || (other instanceof MyAdvisedSupport.MethodCacheKey &&
                    this.method == ((MyAdvisedSupport.MethodCacheKey) other).method));
        }

        @Override
        public int hashCode() {
            return this.hashCode;
        }

        @Override
        public String toString() {
            return this.method.toString();
        }

        @Override
        public int compareTo(MyAdvisedSupport.MethodCacheKey other) {
            int result = this.method.getName().compareTo(other.method.getName());
            if (result == 0) {
                result = this.method.toString().compareTo(other.method.toString());
            }
            return result;
        }
    }
}